import 'dotenv/config';
import { configDefaults, defineProject } from 'vitest/config';
import * as fs from 'fs/promises';
import * as path from 'path';
import { fileURLToPath } from 'url';
import react from '@vitejs/plugin-react';
import { Plugin, transformWithEsbuild } from 'vite';
import { playwright } from '@vitest/browser-playwright';
import { BrowserInstanceOption } from 'vitest/node';

function forceJsxForJsFiles(): Plugin {
  return {
    name: 'force-jsx-loader-for-js',
    enforce: 'pre',
    async transform(code, id) {
      if (id.includes('/node_modules/')) {
        return null;
      }

      if (!id.endsWith('.js')) {
        return null;
      }

      const result = await transformWithEsbuild(code, id, {
        loader: 'jsx',
      });

      // @vitejs/plugin-react only adds the React import for .jsx files.
      if (!result.code.includes("from 'react'") && !result.code.includes('from "react"')) {
        result.code = `import * as React from 'react';\n${result.code}`;
      }

      return result;
    },
  };
}

function getVitestEnvironment(fileName: string): 'browser' | 'node' {
  const basename = path.basename(fileName);
  if (basename.includes('.browser.')) {
    return 'browser';
  }
  return 'node';
}

const MONOREPO_ROOT = path.resolve(__dirname, '.');

export interface CreateOptions {
  jsdom?: boolean;
  enableScrollbars?: boolean;
}

export default async function create(
  fileUrl: string,
  { jsdom = false, enableScrollbars = false }: CreateOptions = {},
) {
  const file = fileURLToPath(fileUrl);
  const testEnv = getVitestEnvironment(file);
  const pkgJson = path.resolve(file, '../package.json');
  const pkg = await fs.readFile(pkgJson, 'utf8').then((content) => JSON.parse(content));

  const name = `${testEnv}:${pkg.name}`;

  const ignore = await fs.readFile(path.resolve(MONOREPO_ROOT, './.gitignore'), 'utf8');
  const excludes = ignore
    .trim()
    .split('\n')
    .filter((line) => line.startsWith('#'))
    .map((line) => (line.startsWith('/') ? line.slice(1) : line));

  return defineProject({
    plugins: [react(), forceJsxForJsFiles()],
    define: {
      'process.env.NODE_ENV': JSON.stringify('test'),
    },
    test: {
      name,
      exclude: ['**/node_modules/**', '**/build/**', '**/*.spec.*', '**/.next/**', ...excludes],
      globals: true,
      disableConsoleIntercept: true,
      setupFiles: [
        path.resolve(MONOREPO_ROOT, './packages-internal/test-utils/src/setupVitest.ts'),
        ...(jsdom || testEnv === 'browser'
          ? [
              path.resolve(
                MONOREPO_ROOT,
                './packages-internal/test-utils/src/setupVitestBrowser.ts',
              ),
            ]
          : []),
      ],
      environment: jsdom ? 'jsdom' : 'node',

      fakeTimers: {
        // We use performance.now in the codebase
        toFake: [...(configDefaults.fakeTimers.toFake ?? []), 'performance'],
      },
      browser: {
        enabled: testEnv === 'browser',
        provider: playwright({
          launchOptions: {
            ignoreDefaultArgs: [...(enableScrollbars ? ['--hide-scrollbars'] : [])],
          },
        }),
        headless: true,
        viewport: {
          width: 1024,
          height: 896,
        },
        instances: (process.env.VITEST_BROWSERS || 'chromium')
          .split(',')
          .map((browser) => ({ browser }) as BrowserInstanceOption),
        screenshotFailures: false,
      },
      env: {
        VITEST: 'true',
      },
    },
    resolve: {
      dedupe: ['react', 'react-dom'],
      alias: {
        '@mui/internal-test-utils': path.resolve(
          MONOREPO_ROOT,
          './packages-internal/test-utils/src',
        ),
        '@mui/internal-docs-utils': path.resolve(
          MONOREPO_ROOT,
          './packages-internal/docs-utils/src',
        ),
        '@mui/material': path.resolve(MONOREPO_ROOT, './packages/mui-material/src'),
        '@mui/system': path.resolve(MONOREPO_ROOT, './packages/mui-system/src'),
        '@mui/types': path.resolve(MONOREPO_ROOT, './packages/mui-types/src'),
        '@mui/utils': path.resolve(MONOREPO_ROOT, './packages/mui-utils/src'),
        '@mui/styled-engine': path.resolve(MONOREPO_ROOT, './packages/mui-styled-engine/src'),
        '@mui/styled-engine-sc': path.resolve(MONOREPO_ROOT, './packages/mui-styled-engine-sc/src'),
        '@mui/styles': path.resolve(MONOREPO_ROOT, './packages/mui-styles/src'),
        '@mui/stylis-plugin-rtl': path.resolve(
          MONOREPO_ROOT,
          './packages/mui-stylis-plugin-rtl/src',
        ),
        '@mui/icons-material': path.resolve(MONOREPO_ROOT, './packages/mui-icons-material/lib/esm'),
        '@mui/lab': path.resolve(MONOREPO_ROOT, './packages/mui-lab/src'),
        '@mui/private-theming': path.resolve(MONOREPO_ROOT, './packages/mui-private-theming/src'),
        '@mui/joy': path.resolve(MONOREPO_ROOT, './packages/mui-joy/src'),
        '@mui/docs': path.resolve(MONOREPO_ROOT, './packages/mui-docs/src'),
        '@mui/material-nextjs': path.resolve(MONOREPO_ROOT, './packages/mui-material-nextjs/src'),
        docs: path.resolve(MONOREPO_ROOT, './docs'),
      },
    },
    optimizeDeps: {
      esbuildOptions: {
        plugins: [
          {
            name: 'js-as-jsx',
            setup(build) {
              build.onLoad({ filter: /\.js$/ }, async (args) => {
                if (args.path.includes('/node_modules/')) {
                  return null;
                }

                const contents = await fs.readFile(args.path, 'utf8');

                return {
                  contents,
                  loader: 'jsx',
                };
              });
            },
          },
        ],
      },
    },
  });
}
